/*
 * Individual.cpp
 *
 *  Created on: Jan 9, 2012
 *      Author: luiz
 */

#include <iostream>
#include <algorithm>
#include <math.h>
#include <iterator>
#include <sstream>
using namespace std;

#include "Individual.h"
#include "Layer.h"
#include "Output.h"
#include "Circuit.h"
#include "Param.h"
#include "GeneCell.h"
#include "RandomNumber.h"
#include "Chromosome.h"
#include "../Model/ChromosomeDecoder.h"
#include "../Model/DecoderCircuitAdapter.h"
#include "../Model/CrossoverPathRepresentationPMX.h"
#include "../Model/CrossoverBinaryRepresentationPoint.h"
#include "../Model/EvaluationFunction.h"


Individual::Individual(int chromossome_size)
{
    chromosome_ = new Chromosome(chromossome_size);
    fitness_ = -1;
    layer_ = NULL;
    traces_ = new std::vector<std::vector<Samples> > ();
    polarization_error_ = -1;
}

Individual::~Individual()
{
    if(chromosome_!=NULL)
    {
        delete chromosome_;
    }
    else
    {
        std::cout << "\n*** ALGUEM DELETOU O CROSSOMO ANTES!!! ***" << std::endl;
        exit(1);
    }
    if(layer_!=NULL){
        delete layer_;
    }
    delete traces_;
}

Individual::Individual(const Individual & individual)
{
    chromosome_ = new Chromosome(*individual.chromosome_);
    fitness_ = individual.fitness_;
    layer_ = NULL;
    traces_ = new std::vector<std::vector<Samples> > (*individual.traces_);
    polarization_error_ = individual.polarization_error_;
    if(individual.layer_!=NULL)
        layer_= new Layer(*individual.layer_);

}



Individual & Individual::operator =(const Individual & individual)
{
    if(this != & individual){
        if(chromosome_!=NULL)
            delete chromosome_;
        chromosome_ = new Chromosome(*individual.chromosome_);
        fitness_ = individual.fitness_;
        if(layer_ !=NULL){
            delete layer_;
            layer_ = NULL;
        }
        if(individual.layer_!=NULL)
            layer_= new Layer(*individual.layer_);
        delete traces_;
        traces_ = new std::vector<std::vector<Samples> > (*individual.traces_);
        polarization_error_ = individual.polarization_error_;

    }
    return *this;
}



void Individual::calculate_fitness(Instance &instance)
{

    ChromosomeDecoder decoder;

    Layer layer = decoder.getChromossomeDecoded(instance,*chromosome_);

    Circuit circuito(&layer,instance.truth_table());

    std::vector<std::vector<Samples> > results = circuito.execute_circuit(instance);

    polarization_error_ = calculate_polarization_error(instance,results);



}


void Individual::generate_random_chromosome()
{
    chromosome_->generate_random_gene();
}

std::vector<int> Individual::convert_gene_cell_postition_to_int_vector(const std::vector<GeneCell> &vector_gene_cell)  {
    std::vector<int> return_vector;
    for (int i = 0; i < (int)vector_gene_cell.size(); ++i) {
        return_vector.push_back(vector_gene_cell[i].position_num());
    }
    return return_vector;
}
std::vector<int> Individual::convert_gene_cell_different_clock_zone_to_int_vector(const std::vector<GeneCell> &vector_gene_cell)  {
    std::vector<int> return_vector;
    for (int i = 0; i < (int)vector_gene_cell.size(); ++i) {
        return_vector.push_back(vector_gene_cell[i].different_clock());
    }
    return return_vector;
}

std::vector<Individual> Individual::crossover(Individual & individual)
{
    //

    //obtem um vetor de inteiros com as posicoes ordenado de acordo com o cromossomo
    std::vector<int> parent_a_positions = convert_gene_cell_postition_to_int_vector(this->get_chromossome().get_gene_cells());
    std::vector<int> parent_b_positions = convert_gene_cell_postition_to_int_vector(((Chromosome)individual.get_chromossome()).get_gene_cells());

    // instancia o crossover
    CrossoverPathRepresentationPMX crossover_positions;
    std::vector< std::vector<int> > offsprings_positions = crossover_positions.execute(parent_a_positions,parent_b_positions);

    //obtem um vetor de inteiros com as zonas de clock (binario) ordenado de acordo com o cromossomo

    // std::vector<int> parent_a_different_clock_zone = convert_gene_cell_different_clock_zone_to_int_vector(this->get_chromossome().get_gene_cells());
    // std::vector<int> parent_b_different_clock_zone = convert_gene_cell_different_clock_zone_to_int_vector(individual.get_chromossome().get_gene_cells());

    // executa o crossover binario
    // CrossoverBinaryRepresentationPoint crossover_different_clock_zone;
    // std::vector< std::vector<int> > offsprings_different_clock_zone = crossover_different_clock_zone.execute(parent_a_different_clock_zone,parent_b_different_clock_zone);

    // cria dois novos individuos, iguais aos pais
    std::vector<Individual> offsprings;
    offsprings.push_back(Individual(*this->chromosome_,*this->layer_));
    offsprings.push_back(Individual(individual.get_chromossome(),*individual.layer()));


    // altera os cromossomos para os novos
    Chromosome chromossome_offspring_0 (offsprings[0].get_chromossome());
    chromossome_offspring_0.define_gene_order(offsprings_positions[0]);
    offsprings[0].set_chromossome(chromossome_offspring_0);

    Chromosome chromossome_offspring_1 (offsprings[1].get_chromossome());
    chromossome_offspring_1.define_gene_order(offsprings_positions[1]);
    offsprings[1].set_chromossome(chromossome_offspring_1);

    // std::cout << "ERROR 0:"<< offsprings[0].print() << std::endl;
    //std::cout << "ERROR 1:"<< offsprings[1].print() << std::endl;

    return offsprings;

}

Chromosome Individual::get_chromossome() const
{
    return *chromosome_;
}

Individual Individual::mutation(double tax)
{
    Individual individual_result(*chromosome_,*layer_);
    std::vector<GeneCell> gene_list = individual_result.get_chromossome().get_gene_cells();
    for (int i = 0; i < (int)gene_list.size(); ++i) {
        double dice = RandomNumber::get_instance().getRandonDouble();
        if(dice < tax){
            int gene_pos_1 = i;
            int gene_pos_2 = RandomNumber::get_instance().getRandomInteger((int)gene_list.size());
            GeneCell gene_1 = gene_list[gene_pos_1];
            gene_list[gene_pos_1] = gene_list[gene_pos_2];
            gene_list[gene_pos_2] = gene_1;
            //individual_result.get_chromossome()->get_gene_cells().at(i).set_different_clock((individual_result.get_chromossome()->get_gene_cells().at(i).different_clock()+1)%2);
        }
    }
    Chromosome chromossome(individual_result.get_chromossome());
    chromossome.set_gene_cells(gene_list);
    individual_result.set_chromossome(chromossome);
    return individual_result;
    //   return
}

bool Individual::operator ()(const Individual & a, const Individual & b)
{
    return a.fitness_ < b.fitness_;
}

bool Individual::operator <(const Individual & b) const
{
    return fitness_ < b.fitness_;
}

Layer Individual::calculate_fitness_with_layer_default(Instance * instance, Layer *layer_default,Individual *better_individual)
{
    double alfa_value = 0;
    ChromosomeDecoder decoder;
    this->set_layer(decoder.getChromossomeDecodedByLayerDefault(*instance,*chromosome_,*layer_default));

    // SE NAO FOR CONVEXO
    if(!layer_->verify_conex()){

        EvaluationFunction *function = instance->evaluation_function();
        polarization_error_ =  function->getValue(2)*instance->entry_table()->num_entrys();

    } else if(this->traces_->empty()) {

        Circuit circuito(layer_,instance->truth_table());

        std::vector<std::vector<Samples> > results = circuito.execute_circuit(*instance);

        polarization_error_ = calculate_polarization_error(*instance,results);

        set_traces(results);
    }

    if(better_individual != NULL)
    {
        fitness_ = (( layer_->get_number_of_cells()/(double) better_individual->layer_number_of_cells() )*alfa_value) + ((1-alfa_value)*( polarization_error_ / (double)better_individual->polarization_error()));
    } else {
        fitness_ = (1*alfa_value) + ((1-alfa_value)*1);
    }
    return *layer_;
}

double Individual::fitness()
{
    return fitness_;
}


void Individual::set_chromossome(const Chromosome & chromossome)
{
    *this->chromosome_ = chromossome;
}

void Individual::set_layer(const Layer & layer)
{
    if(layer_!=NULL){
        delete layer_;
    }
    layer_ = new Layer(layer);
}

Layer* Individual::layer()
{
    return this->layer_;
}

std::string Individual::print()
{
    stringstream oss;
    oss << "INDIVIDUAL FITNESS:" << fitness_  << std::endl;
    oss << chromosome_->print_chromosome();
    return oss.str();
}

void Individual::set_traces(const std::vector<std::vector<Samples> >& traces)
{
    *traces_ = traces;
}

std::vector<std::vector<Samples> > Individual::traces()
{
    return *traces_;
}

double Individual::polarization_error() const
{
    return polarization_error_;
}



double Individual::calculate_polarization_error(Instance & instance, std::vector<std::vector<Samples> > &results)
{
    /*
     * PARA CADA SEQUENCIA DA TABELA VERDADE
     * PEGAR AS AMOSTRAR E OBTER ERRO
     */
    double error=0;
    double non_linear_error=0;
    double expected_value=0;
    double repetition_factor=0;

    // OBTEM TODAS AS CELULAS OUTPUTS
    std::vector<Cell*> output_cell_list = layer_->get_list_cells_by_function(layer_,Cell::OUTPUT_CELL_FUNCTION);

    // PRA CADA CELULA DE SAIDA
    for (int i = 0; i < (int)output_cell_list.size(); ++i) {


        // OBTEM O INDICE DA CELULA OUTPUT CONFORME O NOME
        //  std::cout << "CELL_NAME:" << output_cell_list[i]->cell_name() << std::endl;
        int cell_output_id = instance.truth_table()->get_output_entry_by_name(output_cell_list[i]->cell_name());

        // RECUPERA A QUANTIDADE DE ENTRADAS PARA TESTE
        int number_entrys_test = instance.entry_table()->num_entrys();

        // PARA CADA GRUPO DE AMOSTRAS DA SAIDA
        for(int j = 0; j < number_entrys_test;++j)
        {
            // CALCULA O ERRO DE CADA AMOSTRA ESPERADA
            expected_value = instance.entry_table()->get_output_entry_value(cell_output_id,j);
            repetition_factor = instance.truth_table()->get_output_occurrences_by_value(expected_value);
            //repetition_factor = pow(repetition_factor,2);
            non_linear_error = results[i][j].getEvaluationFunctionError(expected_value,repetition_factor,instance.evaluation_function());
            error += non_linear_error;
        }

    }
    return error;
}

void Individual::set_polarization_error(const double & polarization_error)
{
    this->polarization_error_ = polarization_error;
}

int Individual::layer_number_of_cells() const
{
    return layer_->get_number_of_cells();
}

const Layer *Individual::layer() const
{
    return layer_;
}

void Individual::recalculate_fitness(const double & alfa_value,Individual *better_individual)
{

    if(better_individual != NULL)
    {
        fitness_ = (( layer_->get_number_of_cells()/(double) better_individual->layer_number_of_cells() )*alfa_value) + ((1-alfa_value)*( polarization_error_ / (double)better_individual->polarization_error()));
    } else {
        fitness_ = (1*alfa_value) + ((1-alfa_value)*1);
    }

}

Individual::Individual(const Chromosome & chromosome, const Layer & layer, const std::vector<std::vector<Samples> > & traces)
{
    chromosome_ = new Chromosome(chromosome);
    fitness_ = -1;
    layer_ = new Layer(layer);
    traces_ = new std::vector<std::vector<Samples> > (traces);
    polarization_error_ = -1;

}

bool Individual::is_valid(Instance & instance) const
{
    if(layer_!= NULL && layer_->verify_conex()){

        if(traces_!=NULL){

            /*
             * PARA CADA SEQUENCIA DA TABELA VERDADE
             * PEGAR AS AMOSTRAR E OBTER ERRO
             */

            // OBTEM TODAS AS CELULAS OUTPUTS
            std::vector<Cell*> output_cell_list = layer_->get_list_cells_by_function(layer_,Cell::OUTPUT_CELL_FUNCTION);

            // PRA CADA CELULA DE SAIDA
            for (int i = 0; i < (int)output_cell_list.size(); ++i) {

                // OBTEM O INDICE DA CELULA OUTPUT CONFORME O NOME
                //  std::cout << "CELL_NAME:" << output_cell_list[i]->cell_name() << std::endl;
                int cell_output_id = instance.truth_table()->get_output_entry_by_name(output_cell_list[i]->cell_name());

                // RECUPERA A QUANTIDADE DE ENTRADAS PARA TESTE
                int number_entrys_test = instance.entry_table()->num_entrys();

                // PARA CADA GRUPO DE AMOSTRAS DA SAIDA
                for(int j = 0; j < number_entrys_test;++j)
                {
                    for(int k = 0; k < traces_->at(i)[j].getSize(); k++) {

                        double expected_value = instance.entry_table()->get_output_entry_value(cell_output_id,j);

                        if( fabs(traces_->at(i)[j][k]-expected_value) > 0.5 ){

                            return false;
                        }

                    }

                }

            }

            return true;

        }

    }

    return false;

}



















